{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# [アブストラクトファクトリ](http://morizyun.github.io/ruby/design-pattern-abstract-factory.html)\n",
    "\n",
    "* アブストラクトファクトリパターン\n",
    "    * 矛盾のないオブジェクトの生成を行うためのパターン\n",
    "    * ファクトリのクライアントとなるオブジェクトが、ファクトリオブジェクトにインスタンスの生成を委譲するという関係\n",
    "        * FrogAndAlgaeFactoryはOrganismFactoryにインスタンスの生成を委譲している\n",
    "    * メリット\n",
    "        * 関連しあうオブジェクトの集まりを生成できる\n",
    "            * カエルと藻、アヒルとスイレン\n",
    "        * 整合性が必要となるオブジェクト群を誤りなしに生成できる\n",
    "            * カエルとスイレンという組み合わせは作られない\n",
    "    * 構成要素\n",
    "        * AbstractFactory\n",
    "            * ConcreteFactoryの共通部分の処理を行う\n",
    "                * ここではOrganismFactoryクラス\n",
    "            * <font color=\"red\">コンストラクタの呼び出しをこのAbstractFactoryに集中させる</font>ことで依存度を下げる\n",
    "        * ConcreteFactory\n",
    "            * 実際にオブジェクトの生成を行う\n",
    "                * ここではFrogAndAlgaeFactoryクラスとDuckAndWaterLilyFactoryクラス\n",
    "                * これらのクラスは<font color=\"red\">コンストラクタを呼び出してない</font>\n",
    "        * Product\n",
    "            * ConcreteFactoryによって生成される側のオブジェクト\n",
    "                * ここではDuck,Frog,Algae,WaterLilyクラス\n",
    "\n",
    "* [ファクトリ](http://think-on-object.blogspot.jp/2011/11/factoryfactory-methodabstract-factory.html#what)\n",
    "    * 工場\n",
    "        * オブジェクトの工場\n",
    "        * オブジェクトのインスタンスを生成する工場\n",
    "        * aクラスのインスタンスを生成するためのaファクトリ\n",
    "    * メリット\n",
    "        * オブジェクトのインスタンスを作る箇所での、依存度を下げることができる"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 例\n",
    "\n",
    "* 動物を表すクラス\n",
    "    * アヒルを表すDuckクラスは、食事(eat)メソッドを持っている\n",
    "    * カエルを表すFrogクラスは、食事(eat)メソッドを持っている\n",
    "* 植物を表すクラス\n",
    "    * 藻を表すAlgaeクラスは、成長(grow)メソッドを持っている\n",
    "    * スイレンを表すWaterLilyクラスは、成長(grow)メソッドを持っている\n",
    "* 池の生態系を生成するクラス\n",
    "    * コンストラクタで動物と植物を定義する\n",
    "    * 動物、植物のオブジェクトを返すメソッドを持っている\n",
    "    * 池の環境(動物と植物の組み合わせ)は次の2種類のみが許されている\n",
    "        * DuckとWaterLily\n",
    "        * FrogとAlgae\n",
    "    * <font color=\"red\">この規約を守ること。矛盾の無いオブジェクトの組み合わせを作る</font>のがAbstract Factoryパターン"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       ":eat"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### アヒルとカエルクラス\n",
    "# アヒル\n",
    "class Duck\n",
    "  def initialize(name)\n",
    "    @name = name\n",
    "  end\n",
    "  \n",
    "  # 食べる(eat)\n",
    "  def eat\n",
    "    puts \"アヒル #{@name} は食事中です\"\n",
    "  end\n",
    "end\n",
    "\n",
    "# カエル\n",
    "class Frog\n",
    "  def initialize(name)\n",
    "    @name = name\n",
    "  end\n",
    "  \n",
    "  # 食べる(eat)\n",
    "  def eat\n",
    "    puts \"カエル #{@name} は食事中です\"\n",
    "  end\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       ":grow"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### 藻とスレインクラス\n",
    "# 植物/藻\n",
    "class Algae\n",
    "  def initialize(name)\n",
    "    @name = name\n",
    "  end\n",
    "  \n",
    "  def grow\n",
    "    puts \"藻 #{@name} は成長中です\"\n",
    "  end\n",
    "end\n",
    "\n",
    "# 植物/スイレン\n",
    "class WaterLily\n",
    "  def initialize(name)\n",
    "    @name = name\n",
    "  end\n",
    "  \n",
    "  def grow\n",
    "    puts \"スイレン #{@name} は成長中です\"\n",
    "  end\n",
    "end\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       ":new_plant"
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### 池の生態系を作る (Abstract Factory) : ConcreteFactoryの共通部分の処理を行う\n",
    "class OrganismFactory\n",
    "  def initialize(number_animals, number_plants)\n",
    "\n",
    "    @animals = []\n",
    "    # 池の動物を定義\n",
    "    number_animals.times do |i|\n",
    "      animal = new_animal(\"動物 #{i}\")\n",
    "      @animals << animal\n",
    "    end\n",
    "    \n",
    "    @plants = []\n",
    "    # 池の植物を定義\n",
    "    number_plants.times do |i|\n",
    "      plant = new_plant(\"植物 #{i}\")\n",
    "      @plants << plant\n",
    "    end\n",
    "    \n",
    "    # 動物についてのオブジェクトを返す\n",
    "    def get_animals\n",
    "      @animals\n",
    "    end\n",
    "    \n",
    "    # 植物についてのオブジェクトを返す\n",
    "    def get_plants\n",
    "      @plants\n",
    "    end\n",
    "  end\n",
    "end\n",
    "  \n",
    "# カエル(Frog)と藻(Algae)の生成を行う (Concrete Factory) : 実際にオブジェクトの生成を行う\n",
    "class FrogAndAlgaeFactory < OrganismFactory\n",
    "  private\n",
    "\n",
    "  def new_animal(name)\n",
    "    Frog.new(name)\n",
    "  end\n",
    "\n",
    "  def new_plant(name)\n",
    "    Algae.new(name)\n",
    "  end\n",
    "end\n",
    "\n",
    "# アヒル(Duck)とスイレン(WaterLily)の生成を行う(Concrete Factory) : 実際にオブジェクトの生成を行う\n",
    "class DuckAndWaterLilyFactory < OrganismFactory\n",
    "  private\n",
    "\n",
    "  def new_animal(name)\n",
    "    Duck.new(name)\n",
    "  end\n",
    "\n",
    "  def new_plant(name)\n",
    "    WaterLily.new(name)\n",
    "  end\n",
    "end"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "カエル 動物 0 は食事中です\n",
      "カエル 動物 1 は食事中です\n",
      "カエル 動物 2 は食事中です\n",
      "カエル 動物 3 は食事中です\n",
      "藻 植物 0 は成長中です\n",
      "\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[#<Algae:0x007f31e5e900c0 @name=\"\\u690D\\u7269 0\">]"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "### factoryにはカエルが4匹、藻が1つ入る\n",
    "factory = FrogAndAlgaeFactory.new(4,1)\n",
    "\n",
    "### animalsにはカエルが4匹入る\n",
    "animals = factory.get_animals\n",
    "\n",
    "### 4匹のカエルが食事をする\n",
    "animals.each{|animal| animal.eat}\n",
    "\n",
    "### plansに藻が一つ入り、成長する\n",
    "plants = factory.get_plants\n",
    "plants.each{|plant| plant.grow}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "collapsed": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "アヒル 動物 0 は食事中です\n",
      "アヒル 動物 1 は食事中です\n",
      "アヒル 動物 2 は食事中です\n",
      "スイレン 植物 0 は成長中です\n",
      "スイレン 植物 1 は成長中です\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "[#<WaterLily:0x007f31e5d26c48 @name=\"\\u690D\\u7269 0\">, #<WaterLily:0x007f31e5d26b80 @name=\"\\u690D\\u7269 1\">]"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "factory = DuckAndWaterLilyFactory.new(3,2)\n",
    "animals = factory.get_animals\n",
    "animals.each{|animal| animal.eat}\n",
    "plants = factory.get_plants\n",
    "plants.each{|plant| plant.grow}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<img src=\"img/AbstractFactory01.png\" alt=\"AbstractFactory\" title=\"AbstractFactory\"/>"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ruby 2.3.3",
   "language": "ruby",
   "name": "ruby"
  },
  "language_info": {
   "file_extension": ".rb",
   "mimetype": "application/x-ruby",
   "name": "ruby",
   "version": "2.3.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
